package dbmodel

import (
	"fmt"
	"github.com/jinzhu/gorm"
	"github.com/pkg/errors"
	"myCloud/utils"
	"strconv"
	"strings"
	"time"
)

type PictureFile struct {
	gorm.Model
	Timestamp time.Time
	Width     int
	Height    int
	Camera    string
	CModel    string
	Latitude  float64
	Longitude float64
	Altitude  float64
}

/*
从一张图片中，可以取到哪些值
*/
type PictureExifInfo struct {
	FileName   string
	ShotInfo   *ExifShotInfo
	DeviceInfo *ExifDeviceInfo
	SizeInfo   *ExifSizeInfo
	GpsInfo    *ExifGpsInfo
}

func (m *PictureExifInfo) String() string {
	var sep = fmt.Sprintf("===[%s]===\n", m.FileName)
	var out = sep

	var sInfo = m.ShotInfo
	if sInfo != nil {
		out += fmt.Sprintf("%s %s ISO%d %.2f %d\n",
			sInfo.ExposureTime, sInfo.FNumber, sInfo.IsoSpeed, sInfo.FocalLength, sInfo.Flash,
		)
	}

	var dInfo = m.DeviceInfo
	if dInfo != nil {
		out += fmt.Sprintf("%s %s\n", dInfo.Make, dInfo.CModel)
		if dInfo.DateTime != nil {
			out += fmt.Sprintf("captured at: %s\n", utils.TimerPtrToString(dInfo.DateTime))
		}
	}

	if m.SizeInfo != nil {
		out += fmt.Sprintf("orientation: %d\n", m.SizeInfo.Orientation)
	}

	var gInfo = m.GpsInfo
	if m.GpsInfo != nil {
		out += fmt.Sprintf("Lng: %f, Lat: %f, Alt: %f\n",
			gInfo.Longitude, gInfo.Latitude, gInfo.Altitude,
		)
		if gInfo.DateTime != nil {
			out += fmt.Sprintf("captured at: %s\n", utils.TimerPtrToString(gInfo.DateTime))
		}
	}

	out += sep
	return out
}

func getDigitSub(s string) []string {
	var out []string
	var item string
	for _, w := range s {
		if w >= 0x30 && w <= 0x39 {
			// 数字
			item += string(w)
			continue
		}

		// 不是数字时
		if item != "" {
			out = append(out, item)
			item = ""
		}
	}

	if item != "" {
		out = append(out, item)
	}

	return out
}

func parseDigitTimestamp(example, layout, s string) *time.Time {
	if len(s) != len(example) {
		return nil
	}

	t, err := time.ParseInLocation(layout, s, time.Local)
	if err != nil {
		return nil
	}

	return &t
}

func parseUnixMillsec(s string) *time.Time {
	if len(s) == len("1552434371631") {
		i, err := strconv.Atoi(s)
		if err != nil {
			return nil
		}

		t := time.Unix(int64(i / 1000), 0)
		return &t
	}

	return nil
}

func parseUnixTime(s string) *time.Time {
	if len(s) == len("1552434371") {
		i, err := strconv.Atoi(s)
		if err != nil {
			return nil
		}

		t := time.Unix(int64(i / 1000), 0)
		return &t
	}

	return nil
}

// 根据文件名猜解日期时间，需要先去除常见的分隔符 点、空格，横杠，下划线
func parseTimestamp(fileName string) (*time.Time, error) {
	f := strings.ReplaceAll(fileName, ".", "")
	f = strings.ReplaceAll(f, " ", "")
	f = strings.ReplaceAll(f, "-", "")
	f = strings.ReplaceAll(f, "_", "")

	digits := getDigitSub(f)
	if len(digits) == 0 {
		return nil, errors.New("datetime stamp must be digit")
	}

	// 对每一段连续的数字进行分析
	for _, item := range digits {
		t := parseDigitTimestamp("20060102150405000", "20060102150405000", item)
		if t != nil {
			return t, nil
		}

		t = parseDigitTimestamp("20060102150405", "20060102150405", item)
		if t != nil {
			return t, nil
		}

		t = parseUnixMillsec(item)
		if t != nil {
			return t, nil
		}

		t = parseUnixTime(item)
		if t != nil {
			return t, nil
		}

		t = parseDigitTimestamp("20060102", "20060102", item)
		if t != nil {
			return t, nil
		}
	}

	return nil, errors.New("cannot parse timestamp")
}

/*
根据多个条件 判断文件创建日期时间
1. Exif 信息
2. Exif GPS 信息
3. 文件名 20200802175827 时间戳
4. 文件名 unix 时间戳 毫秒/秒
*/
func (m *PictureExifInfo)Timestamp() *time.Time {
	devInfo := m.DeviceInfo
	if devInfo != nil && devInfo.DateTime != nil {
		return devInfo.DateTime
	}

	gInfo := m.GpsInfo
	if gInfo != nil && gInfo.DateTime != nil {
		return gInfo.DateTime
	}

	// exif 信息完全没有日期时间信息，则从文件名取时间信息
	t, err := parseTimestamp(m.FileName)
	if err != nil {
		return nil
	}

	return t
}

/*
小米图片APP信息显示
f/2.0 1/1704 ISO101
3.79mm flashUsed
分别为
镜头焦距 FNumber 曝光时间 ExposureTime 感光度 IsoSpeed
拍摄焦距 FocalLength 闪光灯状态 flash
*/
type ExifShotInfo struct {
	ExposureTime string
	FNumber      string
	IsoSpeed     int
	FocalLength  float64
	Flash        int
}

type ExifDeviceInfo struct {
	DateTime *time.Time
	Make     string
	CModel   string
}

type ExifSizeInfo struct {
	Orientation    int
	Width          int
	Height         int
	XResolution    int
	YResolution    int
	ResolutionUnit int
	FileSize       int
}

type ExifGpsInfo struct {
	DateTime  *time.Time
	Latitude  float64
	Longitude float64
	Altitude  float64
}
